package com.skyline.energy.definition;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.skyline.energy.annotation.Param;
import com.skyline.energy.exception.DaoGenerateException;
import com.skyline.energy.parser.ExpressionParser;
import com.skyline.energy.parser.ParsedExpression;
import com.skyline.energy.utils.CommonUtils;
import com.skyline.energy.utils.Page;

public class BaseCacheDefinition {
	private ParsedExpression parsedKey;
	private Method[] keyGetterMethods;
	private Integer[] keyParameterIndexes;

	private ParsedExpression parsedVkey;
	private Method[] vkeyGetterMethods;
	private Integer[] vkeyParameterIndexes;

	private int pageIndex = -1;

	public ParsedExpression getParsedKey() {
		return parsedKey;
	}

	public Method[] getKeyGetterMethods() {
		return keyGetterMethods;
	}

	public Integer[] getKeyParameterIndexes() {
		return keyParameterIndexes;
	}

	public ParsedExpression getParsedVkey() {
		return parsedVkey;
	}

	public Method[] getVkeyGetterMethods() {
		return vkeyGetterMethods;
	}

	public Integer[] getVkeyParameterIndexes() {
		return vkeyParameterIndexes;
	}

	public int getPageIndex() {
		return pageIndex;
	}

	protected void parseParameterAnnotations(Annotation[][] annotations, Class<?>[] paramTypes,
			Map<String, Integer> paramIndexes) {

		for (int index = 0; index < annotations.length; index++) {
			if (CommonUtils.isTypePage(paramTypes[index])) {
				pageIndex = index;
			}

			for (Annotation annotation : annotations[index]) {
				Class<? extends Annotation> annotationType = annotation.annotationType();
				if (Param.class.equals(annotationType)) {
					Param param = (Param) annotation;
					String value = param.value();
					paramIndexes.put(value, index);
				}
			}

		}

	}

	protected void configCacheKey(String key, String vkey, Class<?>[] paramTypes, Map<String, Integer> paramIndexes,
			Class<?> returnType) throws DaoGenerateException {
		if (StringUtils.isNotEmpty(key)) {
			parsedKey = ExpressionParser.parseExpression(key);
			List<String> keyParameterNames = parsedKey.getParameterNames();
			Object[] keyGettersAndIndexes = CommonUtils.getGettersAndIndexes(keyParameterNames, paramIndexes,
					paramTypes);
			keyGetterMethods = (Method[]) keyGettersAndIndexes[0];
			keyParameterIndexes = (Integer[]) keyGettersAndIndexes[1];
		}

		if (StringUtils.isNotEmpty(vkey) && CommonUtils.isTypeCollection(returnType)) {// 集合版本号
			parsedVkey = ExpressionParser.parseExpression(vkey);
			List<String> vkeyParameterNames = parsedVkey.getParameterNames();
			Object[] vkeyGettersAndIndexes = CommonUtils.getGettersAndIndexes(vkeyParameterNames, paramIndexes,
					paramTypes);
			vkeyGetterMethods = (Method[]) vkeyGettersAndIndexes[0];
			vkeyParameterIndexes = (Integer[]) vkeyGettersAndIndexes[1];
		} else if (StringUtils.isNotEmpty(vkey)) {
			parsedVkey = ExpressionParser.parseExpression(vkey);
			List<String> vkeyParameterNames = parsedVkey.getParameterNames();

			Map<String, Integer> returnIndexes = new HashMap<String, Integer>(1);
			returnIndexes.put("result", 0);
			Class<?>[] retTypes = new Class<?>[] { returnType };

			Object[] vkeyGettersAndIndexes = CommonUtils.getGettersAndIndexes(vkeyParameterNames, returnIndexes,
					retTypes);
			vkeyGetterMethods = (Method[]) vkeyGettersAndIndexes[0];
			vkeyParameterIndexes = (Integer[]) vkeyGettersAndIndexes[1];
		}
	}

	protected void configCacheKey(String key, String vkey, Class<?>[] paramTypes, Map<String, Integer> paramIndexes)
			throws DaoGenerateException {
		// 此方法需要按集合类处理vkey
		configCacheKey(key, vkey, paramTypes, paramIndexes, List.class);
	}

	private String buildSimpleKey(Object[] args, ParsedExpression parsedExpression, Method[] getterMethods,
			Integer[] parameterIndexes) {
		CommonUtils.assertNotNull(parsedExpression, "parsedExpression can't be null while generate a key");

		String oriKey = parsedExpression.getOriginalExpression();
		List<String> parameterNames = parsedExpression.getParameterNames();

		int length = parameterNames.size();

		int lastIndex = 0;
		StringBuilder builder = new StringBuilder();
		for (int i = 0; i < length; i++) {
			int[] indexes = parsedExpression.getParameterIndexes(i);

			int startIndex = indexes[0];
			int endIndex = indexes[1];
			builder.append(oriKey.substring(lastIndex, startIndex));

			Method method = getterMethods[i];
			Integer index = parameterIndexes[i];

			Object value = CommonUtils.fetchVlaue(method, index, args, parameterNames);
			if (value != null) {
				builder.append(value.toString());
			}

			lastIndex = endIndex;
		}

		builder.append(oriKey.substring(lastIndex));

		return builder.toString();
	}

	public String generateCacheKey(Object[] args) {
		String oriKey = buildSimpleKey(args, parsedKey, keyGetterMethods, keyParameterIndexes);

		StringBuilder cacheKey = new StringBuilder();

		cacheKey.append(oriKey);

		Page page = CommonUtils.getPageArgument(args, pageIndex);
		if (page != null) {
			cacheKey.append("-p").append(page.getCurpage());
		}

		return cacheKey.toString();
	}

	public String generateVersionKey(Object[] args) {
		String oriKey = buildSimpleKey(args, parsedVkey, vkeyGetterMethods, vkeyParameterIndexes);

		return oriKey;
	}

	public String generatePageKey(Object[] args, String key) {
		Page page = CommonUtils.getPageArgument(args, pageIndex);
		if (page == null) {
			return null;
		}
		if (StringUtils.isEmpty(key)) {
			key = generateCacheKey(args);
		}
		if (StringUtils.isEmpty(key)) { // 无法创建key
			return null;
		}
		StringBuilder builder = new StringBuilder("page-");
		builder.append(key);
		return builder.toString();
	}
}
